import java.net.*;
import java.io.*;
import java.util.*;

/**
 * The Server Class is the Server Side Component of the Server-Client set up for
 * the JadeTalk Chat System.
 * It provides a ServerSocket on a provided port, to which clients can connect and
 * be assigned a socket and associated ClientHandler
 *
 * @author BluJaded Team
 * @version (see Sourceforge.net)
 */
public class Server {
    
    // Static variable references the server log.
    private static PrintWriter log;

    // switch variable used to terminate the server cleanly
    private static boolean run = true;

    private static boolean logCreated = false;
    
    /**
     * Creates a Server object and begins and infinate loop that will accept any incoming connections
     * and assign them a socket and associated ClientHandler object.
     *
     * @param port  the port number of an available port for use by JadeTalk.
     * @throws java.io.IOException  thrown if the serverSocket fails to initialize
     */
    public Server(int port) throws IOException{

        writeToLog("JadeTalk Server Created - port: "+ port);
        
        // creates a ServerSocket on the provided port
        ServerSocket server = new ServerSocket(port);
        
        // sets a 0.5 second timeout on the server.accept() method
        server.setSoTimeout(500);
        
        // begins a loop that is only left when an exception occurs or when the run variable is set to false
        while (run) {
            Socket client = null;
            
            try{
                // trys to accept a connection from a client
                client = server.accept();     
            } 
            catch (SocketTimeoutException e) {}
            
            // if it successfully accepts a clients connection
            if (client != null){
                System.out.println("Connection to Server accepted from: " + client.getInetAddress());

                // creates a ClientHandler object associated to the Client's socket
                ClientHandler handler = new ClientHandler(client, this);
                
                writeToLog("New Client connection accepted from: " + client.getInetAddress());
                
                // then begins the new ClientHandler as a new Thread.
                handler.start();
                
            }
            // if the Server Gui is in use
            if (ServerGui.Sframe != null){
                if (ServerGui.Sframe.isVisible()) {
                    // update the the value of the connected clients
                    ServerGui.updateClientCount();
                }
            }
        }
        
        // if the switch has been thrown to terminate the server
        if (!run){
            // synchronized access to the handlers collection preventing concurrent access whilst in use
            synchronized (ClientHandler.handlers) {
                
                // creates an iterator for the handlers collection
                Iterator myHandlers = ClientHandler.handlers.iterator();
                
                // iterates through the collection
                while (myHandlers.hasNext()) {
                    ClientHandler handler = (ClientHandler) myHandlers.next();
                    // changes the switch variable "run" to terminate the current ClientHandler thread smoothly
                    handler.terminateHandler();                                        
                }
            }

            writeToLog("Server closed");
            // closes the log file.
            log.close();
            
            // exits any remaining Server proccessing and perfoms garbage collection on associated memory
            System.exit(0);
        }                                                                                          
    }
    
    /**
     * Writes the String parameter to a log file.
     * @param toLog     String to be written to the Server log
     */
    public static void writeToLog(String toLog) {
        // If no log file created yet then create one
        if(!logCreated) {
            try {
            // creates the log file or opens an existing one to append to.
            log = new PrintWriter(new FileWriter("log.txt", true), true);
            }
            catch(IOException e) {}
            logCreated = true;
        }
        // retrieve the current system time
        Calendar dateTime = Calendar.getInstance();
        // print the provided string with a date/time stamp to the log file
        log.println(dateTime.getTime().toString() + " - " + toLog);
    }
    
    /**
     * Switches the switch variable to terminate the server .
     */
    public static void terminateServer()
    {
        run = false;
    }
    
    public static boolean state()
    {
        return run;
    }
    
    /**
     * main method provides an execution start and allows commandline execution.
     * @param args The standard commandline arguments parameter.
     *             The expected argument is a port number of an available port for use by JadeTalk.
     * @throws java.io.IOException  thrown if the Server fails to initialize
     */
    public static void main(String[] args) throws IOException{
        // if no argument is given
        if (args.length != 1){
            // throws an exception prompting for input
            throw new RuntimeException("Please provide the required input. Syntax: <Port>");   
        } 
        else {
            // creates a new Server object, parsing the argument provided as parameter
            new Server(Integer.parseInt(args[0]));                                                        
        }
    }
}